import { GameObject } from './GameObject';
import { Layer } from './Layer';

export interface EngineOptions {
    width?: number;
    height?: number;
    backgroundColor?: string;
    targetFPS?: number;
}

export class Engine {
    private canvas: HTMLCanvasElement;
    private ctx: CanvasRenderingContext2D;
    private layers: Layer[];
    private root: GameObject;
    private lastTime: number;
    private targetFPS: number;
    private frameTime: number;
    private isRunning: boolean;
    private backgroundColor: string;
    
    constructor(canvas: HTMLCanvasElement, options: EngineOptions = {}) {
        this.canvas = canvas;
        const context = canvas.getContext('2d');
        if (!context) {
            throw new Error('Could not get 2D context from canvas');
        }
        this.ctx = context;
        
        // Set canvas size
        this.canvas.width = options.width || 800;
        this.canvas.height = options.height || 600;
        
        // Initialize engine properties
        this.layers = [];
        this.root = new GameObject({ name: 'Root' });
        this.lastTime = 0;
        this.targetFPS = options.targetFPS || 60;
        this.frameTime = 1000 / this.targetFPS;
        this.isRunning = false;
        this.backgroundColor = options.backgroundColor || '#000000';
        
        // Bind methods
        this.update = this.update.bind(this);
        this.render = this.render.bind(this);
        this.gameLoop = this.gameLoop.bind(this);
    }
    
    /**
     * Add a layer to the engine
     */
    public addLayer(layer: Layer): void {
        this.layers.push(layer);
        this.root.addChild(layer);
    }
    
    /**
     * Remove a layer from the engine
     */
    public removeLayer(layer: Layer): void {
        const index = this.layers.indexOf(layer);
        if (index !== -1) {
            this.layers.splice(index, 1);
            this.root.removeChild(layer);
        }
    }
    
    /**
     * Get all layers
     */
    public getLayers(): Layer[] {
        return [...this.layers];
    }
    
    /**
     * Get the root game object
     */
    public getRoot(): GameObject {
        return this.root;
    }
    
    /**
     * Start the game loop
     */
    public start(): void {
        if (this.isRunning) return;
        
        this.isRunning = true;
        this.lastTime = performance.now();
        requestAnimationFrame(this.gameLoop);
    }
    
    /**
     * Stop the game loop
     */
    public stop(): void {
        this.isRunning = false;
    }
    
    /**
     * Set the target FPS
     */
    public setTargetFPS(fps: number): void {
        this.targetFPS = fps;
        this.frameTime = 1000 / fps;
    }
    
    /**
     * Get the current FPS
     */
    public getFPS(): number {
        return 1000 / this.frameTime;
    }
    
    /**
     * Set the background color
     */
    public setBackgroundColor(color: string): void {
        this.backgroundColor = color;
    }
    
    /**
     * Resize the canvas
     */
    public resize(width: number, height: number): void {
        this.canvas.width = width;
        this.canvas.height = height;
    }
    
    /**
     * Get the canvas element
     */
    public getCanvas(): HTMLCanvasElement {
        return this.canvas;
    }
    
    /**
     * Get the canvas context
     */
    public getContext(): CanvasRenderingContext2D {
        return this.ctx;
    }
    
    /**
     * The main game loop
     */
    private gameLoop(currentTime: number): void {
        if (!this.isRunning) return;
        
        // Calculate delta time
        const deltaTime = (currentTime - this.lastTime) / 1000;
        this.lastTime = currentTime;
        
        // Update and render
        this.update(deltaTime);
        this.render();
        
        // Schedule next frame
        requestAnimationFrame(this.gameLoop);
    }
    
    /**
     * Update all game objects
     */
    private update(deltaTime: number): void {
        // Update root object (which updates all children)
        this.root.update(deltaTime);
    }
    
    /**
     * Render all game objects
     */
    private render(): void {
        // Clear canvas
        this.ctx.fillStyle = this.backgroundColor;
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
        
        // Render root object (which renders all children)
        this.root.render(this.ctx);
    }
    
    /**
     * Destroy the engine and clean up resources
     */
    public destroy(): void {
        this.stop();
        this.root.destroy();
        this.layers = [];
    }
} 